#ifndef HIR_INSTRUCTIONS_HPP
#define HIR_INSTRUCTIONS_HPP
#pragma once
#include <vector>
namespace hir
{

#define H_INSTR_LIST(V) \
V(Const) \
V(SetupLoop) \
V(SetupExcept) \
V(SetupFinally) \
V(MakeFunc) \
V(MakeClosure) \
V(SetupWith) \
V(Phi) \
V(UnaryPositive) \
V(UnaryNegative) \
V(UnaryNot) \
V(UnaryConvert) \
V(UnaryInvert) \
V(BinaryPower) \
V(BinaryMultiply) \
V(BinaryDivide) \
V(BinaryModulo) \
V(BinaryAdd) \
V(BinarySubtract) \
V(BinarySubscr) \
V(BinaryFloor_divide) \
V(BinaryTrue_divide) \
V(BinaryLshift) \
V(BinaryRshift) \
V(BinaryAnd) \
V(BinaryXor) \
V(BinaryOr) \
V(Slice) \
V(StoreSlice) \
V(DeleteSlice) \
V(BuildSlice) \
V(LoadLocals) \
V(LoadConst) \
V(LoadName) \
V(LoadAttr) \
V(LoadGlobal) \
V(LoadFast) \
V(LoadClosure) \
V(LoadDeref) \
V(StoreMap) \
V(StoreSubscr) \
V(StoreName) \
V(StoreAttr) \
V(StoreGlobal) \
V(StoreFast) \
V(StoreDeref) \
V(DeleteSubscr) \
V(DeleteName) \
V(DeleteAttr) \
V(DeleteFast) \
V(DeleteGlobal) \
V(GetIter) \
V(PrintExpr) \
V(PrintItem) \
V(PrintNewLine) \
V(PrintItemTo) \
V(PrintNewLineTo) \
V(ReturnValue) \
V(YieldValue) \
V(JumpForward) \
V(JumpIfFalse) \
V(JumpIfTrue) \
V(ContinueLoop) \
V(BreakLoop) \
V(ImportStar) \
V(ExecStatement) \
V(UnpackSequence) \
V(ForIter) \
V(ListAppend) \
V(BuildTuple) \
V(BuildList) \
V(BuildSet) \
V(BuildMap) \
V(BuildClass) \
V(CmpStatement) \
V(ImportName) \
V(ImportFrom) \
V(RaiseStatment) \
V(SetAdd) \
V(MapAdd) \
V(WithCleanup) \
V(PopBlock) \
V(CallFunction) \
V(InplaceAdd) \
V(InplaceSubtract) \
V(InplaceMultiply) \
V(InplaceModulo) \
V(InplaceDivide) \
V(InplacePower) \
V(InplaceLshift) \
V(InplaceRshift) \
V(InplaceAnd) \
V(InplaceXor) \
V(InplaceOr) \
V(InplaceTrueDivide) \
V(InplaceFloorDivide) \
 


enum
{
#define DECLARE_ENUM(x)\
    x##_id ,
    H_INSTR_LIST(DECLARE_ENUM)
};


// FIXME: instruction should be devide to base instruction,no operand instruction ,unary instruction ...
// the operation the class Instruction exposed is low level,and should not be used beyone graph builder
class Instruction
{
public:
    Instruction();
    virtual ~Instruction();

    // the instruction name
    virtual const char * name() const = 0;
    virtual uint32_t id() const = 0;

    // the use list type
    typedef std::vector<uint32_t> UseList;
    // the operand list type
    typedef std::vector<uint32_t> OperandList;
    // get the instruction id using this instruction
    inline uint32_t getUse(uint32_t index) const
    {
        return useList_.at(index);
    }
    // get the use list size
    inline std::size_t getUseSize() const
    {
        return useList_.size();
    }
    // get the instruction id used by this instruction
    inline uint32_t getOperand(uint32_t index) const
    {
        return operandList_.at(index);
    }
    // get the operand list size
    inline std::size_t getOperandSize() const
    {
        return operandList_.size();
    }
    // is the use list empty ?
    inline bool emptyUse() const
    {
        return useList_.empty();
    }
    // is operand list empty
    inline bool emptyOperand() const
    {
        return operandList_.empty();
    }

    // add use
    inline void addUse(uint32_t user)
    {
        useList_.push_back(user);
    }
    // low level add operand
    inline void addOperand(uint32_t operand)
    {
        operandList_.push_back(operand);
    }
    // get graph id
    uint32_t getGraphId() const
    {
        return graphId_;
    }
    // set graph id
    void setGraphId(uint32_t gid)
    {
        graphId_ = gid;
    }
    // get bytecode id bci
    uint32_t getBci() const
    {
        return bci_;
    }
    // set bytecode id bci
    void setBci(uint32_t bci)
    {
        bci_ = bci;
    }

protected:
    // the use list
    UseList useList_;
    // the operand list
    OperandList operandList_;
    // graph id
    uint32_t graphId_;
    // bytecode id bci
    uint32_t bci_;
};

class JumpInstruction : public Instruction
{
public:
    explicit JumpInstruction(uint32_t label);

    // bci label
    uint32_t label_;
};


/********************************************************************************/
// begin meta operation

class Const : public Instruction
{
public:
    explicit Const(PyObject *);

    inline PyObject * object()
    {
        return object_;
    }
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    PyObject * object_;
};

class SetupLoop : public Instruction
{
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class SetupExcept: public Instruction
{
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class SetupFinally : public Instruction
{
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class MakeFunc : public Instruction
{
public:
    explicit MakeFunc(uint32_t codeOperand,uint32_t * defaultAugs,uint32_t defaultAugsCount);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class MakeClosure : public Instruction
{
public:
    explicit MakeClosure(uint32_t codeOperand,uint32_t closureOperand,uint32_t * defaultAugs,uint32_t defaultAugsCount);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

// place holder for lir lowering
class SetupWith : public Instruction
{
public:
    explicit SetupWith();
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class Phi : public Instruction
{
public:
    explicit Phi(uint32_t index);

    inline uint32_t index() const
    {
        return index_;
    }
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t index_;
};

// end meta operation
/********************************************************************************/


/********************************************************************************/
// begin unary operation
#define DECLARE_UNARY_INSTRUCTION(x) \
class Unary##x : public Instruction { \
public: \
    explicit Unary##x(uint32_t operand);\
private: \
    virtual const char * name() const; \
    virtual uint32_t id() const; \
}\
 
DECLARE_UNARY_INSTRUCTION(Positive);
DECLARE_UNARY_INSTRUCTION(Negative);
DECLARE_UNARY_INSTRUCTION(Not);
DECLARE_UNARY_INSTRUCTION(Convert);
DECLARE_UNARY_INSTRUCTION(Invert);

// end unary operation
/********************************************************************************/
// begin binary operation

#define DECLARE_BINARY_INSTRUCTION(x) \
class Binary##x: public Instruction { \
public: \
    explicit Binary##x(uint32_t operand1,uint32_t operand2);\
private: \
    virtual const char * name() const; \
    virtual uint32_t id() const; \
}

DECLARE_BINARY_INSTRUCTION(Power);
DECLARE_BINARY_INSTRUCTION(Multiply);
DECLARE_BINARY_INSTRUCTION(Divide);
DECLARE_BINARY_INSTRUCTION(Modulo);
DECLARE_BINARY_INSTRUCTION(Add);
DECLARE_BINARY_INSTRUCTION(Subtract);
DECLARE_BINARY_INSTRUCTION(Subscr);
DECLARE_BINARY_INSTRUCTION(Floor_divide);
DECLARE_BINARY_INSTRUCTION(True_divide);
DECLARE_BINARY_INSTRUCTION(Lshift);
DECLARE_BINARY_INSTRUCTION(Rshift);
DECLARE_BINARY_INSTRUCTION(And);
DECLARE_BINARY_INSTRUCTION(Xor);
DECLARE_BINARY_INSTRUCTION(Or);
// end binary operation
/********************************************************************************/
// begin slice family
class Slice : public Instruction
{
public:
    explicit Slice(uint32_t operand1,uint32_t operand2 = -1,uint32_t operand3 = -1);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class StoreSlice : public Instruction
{
public:
    explicit StoreSlice(uint32_t operand1,uint32_t operand2,uint32_t operand3,uint32_t operand4);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class DeleteSlice: public Instruction
{
public:
    explicit DeleteSlice(uint32_t operand1,uint32_t operand2,uint32_t operand3);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class BuildSlice : public Instruction
{
public:
    explicit BuildSlice(uint32_t startOperand, uint32_t stopOperand, uint32_t stepOperand);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

// end slice family
/********************************************************************************/
// begin load family

class LoadLocals : public Instruction
{
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class LoadConst : public Instruction
{
public:
    explicit LoadConst(uint32_t oparg);

private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class LoadName : public Instruction
{
public:
    explicit LoadName(uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class LoadAttr : public Instruction
{
public:
    explicit LoadAttr(uint32_t objectOperand,uint32_t oparg);

private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class LoadGlobal : public Instruction
{
public:
    explicit LoadGlobal(uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class LoadFast : public Instruction
{
public:
    explicit LoadFast(uint32_t oparg);

private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class LoadClosure : public Instruction
{
public:
    explicit LoadClosure(uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class LoadDeref : public Instruction
{
public:
    explicit LoadDeref(uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

// end load family
/********************************************************************************/
// begin store family
class StoreMap : public Instruction
{
public:
    explicit StoreMap(uint32_t key,uint32_t value,uint32_t dict);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class StoreSubscr : public Instruction
{
public:
    explicit StoreSubscr(uint32_t dict,uint32_t w,uint32_t u);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class StoreName : public Instruction
{
public:
    explicit StoreName(uint32_t valueOperand,uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class StoreAttr : public Instruction
{
public:
    explicit StoreAttr(uint32_t objectOperand,uint32_t valueOperand,uint32_t oparg);

private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class StoreGlobal : public Instruction
{
public:
    explicit StoreGlobal(uint32_t valueOperand,uint32_t oparg);

private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class StoreFast : public Instruction
{
public:
    explicit StoreFast(uint32_t objectOperand,uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class StoreDeref : public Instruction
{
public:
    explicit StoreDeref (uint32_t objectOperand,uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};


// end store family
/********************************************************************************/
// delete family
class DeleteSubscr : public Instruction
{
public:
    explicit DeleteSubscr(uint32_t dict,uint32_t w);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};


class DeleteName : public Instruction
{
public:
    explicit DeleteName(uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class DeleteAttr : public Instruction
{
public:
    DeleteAttr(uint32_t objectOperand,uint32_t oparg);

private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class DeleteFast : public Instruction
{
public:
    DeleteFast(uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class DeleteGlobal : public Instruction
{
public:
    DeleteGlobal(uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

/********************************************************************************/
// begin iter family
class GetIter : public Instruction
{
public:
    explicit GetIter(uint32_t it);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

// end iter family
/********************************************************************************/
// begin print family
class PrintExpr : public Instruction
{
public:
    explicit PrintExpr(uint32_t expr);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class PrintItem : public Instruction
{
public:
    explicit PrintItem(uint32_t item);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class PrintNewLine : public Instruction
{
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class PrintItemTo : public Instruction
{
public:
    explicit PrintItemTo(uint32_t stream,uint32_t item);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class PrintNewLineTo : public Instruction
{
public:
    explicit PrintNewLineTo(uint32_t stream);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};


// end print family
/********************************************************************************/
// begin control flow control

class ReturnValue : public Instruction
{
public:
    explicit ReturnValue(uint32_t operand);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class YieldValue: public Instruction
{
public:
    explicit YieldValue(uint32_t yield);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class JumpForward : public JumpInstruction
{
public:
    explicit JumpForward(uint32_t label);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class JumpIfFalse : public JumpInstruction
{
public:
    explicit JumpIfFalse(uint32_t objectOperand,uint32_t label);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class JumpIfTrue : public JumpInstruction
{
public:
    explicit JumpIfTrue(uint32_t objectOperand,uint32_t label);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

};

class ForIter : public JumpInstruction
{
public:
    explicit ForIter(uint32_t iterater,uint32_t bci);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

// used to end a try block and jump to ip
class ContinueLoop : public JumpInstruction
{
public:
    explicit  ContinueLoop(uint32_t label);

private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class BreakLoop : public Instruction
{
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

// end control flow control
/********************************************************************************/
// begin statments

class ImportStar: public Instruction
{
public:
    explicit ImportStar(uint32_t str);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class ExecStatement : public Instruction
{
public:
    explicit ExecStatement(uint32_t prog,uint32_t globals,uint32_t local);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class UnpackSequence : public Instruction
{
public:
    explicit UnpackSequence(uint32_t operand,uint32_t index);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t index_;
};

class ListAppend : public Instruction
{
public:
    explicit ListAppend(uint32_t valueOperand,uint32_t args);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};


class BuildTuple : public Instruction
{
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class BuildList : public Instruction
{
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class BuildSet : public Instruction
{
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class BuildMap : public Instruction
{
public:
    explicit BuildMap(uint32_t preservedSize);

private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t preservedSize_;
};

class BuildClass: public Instruction
{
public:
    explicit BuildClass(uint32_t methods, uint32_t bases, uint32_t name);

private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t preservedSize_;
};

class CmpStatement : public Instruction
{
public:
    explicit CmpStatement(uint32_t objectOperand1,uint32_t objectOperand2, uint32_t type);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t type_;
};


class ImportName : public Instruction
{
public:
    explicit ImportName(uint32_t operand1,uint32_t operand2,uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class ImportFrom : public Instruction
{
public:
    explicit ImportFrom(uint32_t moduleOperand,uint32_t oparg);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t oparg_;
};

class RaiseStatment : public Instruction
{
public:
    explicit RaiseStatment(uint32_t exec = -1 ,uint32_t value = -1,uint32_t  traceback = -1);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class SetAdd : public Instruction
{
public:
    explicit SetAdd(uint32_t set,uint32_t operand);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class MapAdd: public Instruction
{
public:
    explicit MapAdd(uint32_t operand1,uint32_t operand2,uint32_t operand3);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

// end statments
/********************************************************************************/
// begin block control
// These hir is needed because I need to determine their side effect
// They will be lowered in lir

class WithCleanup : public Instruction
{
public:
    explicit WithCleanup(uint32_t operand1,uint32_t operand2,uint32_t operand3);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

class PopBlock : public Instruction
{
private:
    virtual const char * name() const;
    virtual uint32_t id() const;
};

// end block control
/********************************************************************************/
// begin call family

class CallFunction : public Instruction
{
public:
    explicit CallFunction(uint32_t argumentCount);
private:
    virtual const char * name() const;
    virtual uint32_t id() const;

    uint32_t argumentCount_;
};

// end call family
/********************************************************************************/
// begin inplace operators
#define DECLARE_INPLACE_INSTRUCTION(x) \
class Inplace##x: public Instruction { \
public: \
    explicit Inplace##x(uint32_t operand1,uint32_t operand2);\
private: \
    virtual const char * name() const; \
    virtual uint32_t id() const; \
}

DECLARE_INPLACE_INSTRUCTION(Add);
DECLARE_INPLACE_INSTRUCTION(Subtract);
DECLARE_INPLACE_INSTRUCTION(Multiply);
DECLARE_INPLACE_INSTRUCTION(Divide);
DECLARE_INPLACE_INSTRUCTION(Modulo);
DECLARE_INPLACE_INSTRUCTION(Power);
DECLARE_INPLACE_INSTRUCTION(Lshift);
DECLARE_INPLACE_INSTRUCTION(Rshift);
DECLARE_INPLACE_INSTRUCTION(And);
DECLARE_INPLACE_INSTRUCTION(Xor);
DECLARE_INPLACE_INSTRUCTION(Or);
DECLARE_INPLACE_INSTRUCTION(TrueDivide);
DECLARE_INPLACE_INSTRUCTION(FloorDivide);


}
// end inplace operators
/********************************************************************************/

#endif /* HIR_INSTRUCTIONS_HPP */

